package foundation.common.util;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.MathContext;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.regex.Pattern;

import javax.imageio.ImageIO;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;

import ar.com.sne.util.DateUtil;




/**
 * Es un helper que provee funciones genericas para manejo de strings, formatos
 * de fechas y numeros.
 * 
 * @author Florencia Maneiro
 * 
 */
public abstract class CommonGeneralUtil {

	private static final long serialVersionUID = 4873428462167843238L;
	public static final String MODOSELECCION = "modoSeleccion";
	public static Pattern EMAIL_PATTERN = Pattern
	.compile("^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,4})+$");
	
	
	public static void throwBussinesException(BusinessException be) {
		// si hay excepciones, lanzarlas, si no, no hace nada
		if ((be.getDescriptions() != null && be.getDescriptions().size() > 0)
				|| (be.getExceptionsKeys() != null && be.getExceptionsKeys()
						.size() > 0)
				|| (be.getExceptionsKeysParameters() != null && be
						.getExceptionsKeysParameters().size() > 0)) {
			throw be;
		}
	}

	public static String substringBefore(String str, String separator) {
		if ((isEmpty(str)) || (separator == null)) {
			return str;
		}
		if (separator.length() == 0) {
			return "";
		}
		int pos = str.indexOf(separator);
		if (pos == -1) {
			return str;
		}
		return str.substring(0, pos);
	}

	public static boolean isEmpty(String str) {
		return ((str == null) || (str.length() == 0));
	}

	public static Date convertirFechaDesde(Date fechaDesde) {
		Calendar cDesde = GregorianCalendar.getInstance();
		cDesde.setTime(fechaDesde);
		cDesde.set(Calendar.AM_PM, 0);
		cDesde.set(Calendar.HOUR, 0);
		cDesde.set(Calendar.MINUTE, 0);
		cDesde.set(Calendar.SECOND, 0);
		cDesde.set(Calendar.MILLISECOND, 0);
		return cDesde.getTime();
	}

	public static Date convertirFechaHasta(Date fechaHasta) {
		Calendar cHasta = GregorianCalendar.getInstance();
		cHasta.setTime(fechaHasta);
		cHasta.set(Calendar.AM_PM, 0);
		cHasta.set(Calendar.HOUR, 23);
		cHasta.set(Calendar.MINUTE, 59);
		cHasta.set(Calendar.SECOND, 59);
		cHasta.set(Calendar.MILLISECOND, 99);
		return cHasta.getTime();
	}

	public static String firstToUpperCase(String txt) {
		String letra = "";
		String nuevoTexto = "";
		String resto = "";
		txt = txt.trim();
		resto = txt.substring(1);
		letra = txt.substring(0, 1);
		letra = letra.toUpperCase();
		nuevoTexto = letra + resto;
		return nuevoTexto;

	}

	public static Boolean esCuitValido(String strCuit) throws Exception {
		// persona = (Persona)this.persistenceService.getById(Persona.class,
		// persona.getOid());
		Boolean resultado = false; // asumir cuit/cuil invalido
		Integer[] tablaMul = { 5, 4, 3, 2, 7, 6, 5, 4, 3, 2 }; // arreglo
		// arbitrario
		Integer[] r = new Integer[11];// arreglo con resultados de multiplicar
		// por la tabla arbitraria
		char[] cuit = strCuit.toCharArray(); // arreglo de char cuyos elementos
		// son los numeros que conforman
		// el CUIT/CUIL

		// si cuit/cuil ingresado no esta formado por 11 digitos es invalido
		if (strCuit.trim().length() == 11) {
			for (int i = 0; i < 10; i++) {
				// multiplico cada digito por la tabla arbitraria menos el
				// ultimo
				r[i] = Integer.valueOf(String.valueOf(cuit[i])) * tablaMul[i];
			}
			int sumatoria = 0;
			for (int i = 0; i < 10; i++) {
				sumatoria = sumatoria + r[i]; // calcula la sumatoria de los
				// resultados
			}

			int dividendo = sumatoria / 11; // Dividir por 11 (division entera)
			int producto = dividendo * 11; // El resultado multiplica por 11
			int diferencia = sumatoria - producto; // Obtener la diferencia

			int digitoVerificador;
			if (diferencia > 0) {
				digitoVerificador = 11 - diferencia;
			} else {
				digitoVerificador = diferencia;
			}
			// si el digito verificador es igual al ultimo digito del cuit/cuil
			// es valido.
			if (digitoVerificador == Integer.valueOf(String.valueOf(cuit[10]))) {
				resultado = true;
			}
		}

		return resultado;
	}

	public static DetachedCriteria addOrderToFilter(DetachedCriteria criteria,
			FinderFilter filter) {
		for (OrderFilter orden : filter.getOrders()) {
			if (orden.getOrderType() == OrderType.Ascending) {
				criteria.addOrder(Order.asc(orden.getOrder()));
			} else if (orden.getOrderType() == OrderType.Descending) {
				{
					criteria.addOrder(Order.desc(orden.getOrder()));
				}
			}
		}
		return criteria;
	}

	/**
	 * Devuelve un digito verificador segun algoritmo de codigo de barras
	 * interleaved 2 de 5
	 * 
	 * @param codigo
	 * @return
	 */
	public static String getDigitoVerificador(String msg) {
		int oddMultiplier = 3;
		int evenMultiplier = 1;
		int oddsum = 0;
		int evensum = 0;
		for (int i = 0; i < msg.length(); i++) {
			if (i % 2 == 0) {
				evensum += Character.digit(msg.charAt(i), 10);
			} else {
				oddsum += Character.digit(msg.charAt(i), 10);
			}
		}
		int check = 10 - ((evensum * oddMultiplier + oddsum * evenMultiplier) % 10);
		if (check >= 10)
			check = 0;
		return Character.toString(Character.forDigit(check, 10));

	}
	
	
	public static int monthsBetween(Date minuend, Date subtrahend) {
		Calendar cal = Calendar.getInstance();
		// default will be Gregorian in US Locales
		cal.setTime(minuend);
		int minuendMonth = cal.get(Calendar.MONTH);
		int minuendYear = cal.get(Calendar.YEAR);
		cal.setTime(subtrahend);
		int subtrahendMonth = cal.get(Calendar.MONTH);
		int subtrahendYear = cal.get(Calendar.YEAR);

		// the following will work okay for Gregorian but will not
		// work correctly in a Calendar where the number of months
		// in a year is not constant
		return ((minuendYear - subtrahendYear) * cal.getMaximum(Calendar.MONTH))
				+ (minuendMonth - subtrahendMonth);
	}

	public static int quincenasBetween(Date minuend, Date subtrahend) {
		Calendar cal = Calendar.getInstance();
		// default will be Gregorian in US Locales
		cal.setTime(minuend);
		int minuendMonth = cal.get(Calendar.MONTH);
		int minuendYear = cal.get(Calendar.YEAR);
		cal.setTime(subtrahend);
		int subtrahendMonth = cal.get(Calendar.MONTH);
		int subtrahendYear = cal.get(Calendar.YEAR);

		// the following will work okay for Gregorian but will not
		// work correctly in a Calendar where the number of months
		// in a year is not constant
		return (((minuendYear - subtrahendYear) > 0 ? (minuendYear - subtrahendYear)
				: 0)
				* cal.getMaximum(Calendar.MONTH) * 2)
				+ (minuendMonth - subtrahendMonth) * 2;
	}

	public static int weeksBetween(Date minuend, Date subtrahend) {
		Calendar cal = Calendar.getInstance();
		// default will be Gregorian in US Locales
		cal.setTime(minuend);
		int minuendWeek = cal.get(Calendar.WEEK_OF_YEAR);
		int minuendYear = cal.get(Calendar.YEAR);
		cal.setTime(subtrahend);
		int subtrahendWeek = cal.get(Calendar.WEEK_OF_YEAR);
		int subtrahendYear = cal.get(Calendar.YEAR);

		// the following will work okay for Gregorian but will not
		// work correctly in a Calendar where the number of months
		// in a year is not constant
		return ((minuendYear - subtrahendYear) * cal.getMaximum(Calendar.MONTH))
				+ (minuendWeek - subtrahendWeek);
	}

	public static BigDecimal getBigDecimal(Double d){
		
		if(d!=null){
			BigDecimal valor = new BigDecimal(d.doubleValue());
			return valor.setScale(6,BigDecimal.ROUND_HALF_EVEN);
		}
		return new BigDecimal(0);		
	}
	
	

	public static Double addDoubles(Double bd1, Double bd2){
		if(bd1==null)
			return bd2.doubleValue();
		if(bd2==null)
			return bd1.doubleValue();
		return getBigDecimal(bd1).add(getBigDecimal(bd2)).doubleValue();
		
	}
	
	public static BigDecimal addBigDecimal(BigDecimal bd1, BigDecimal bd2){
		if(bd1==null)
			return getBigDecimal(bd2.doubleValue());
		if(bd2==null)
			return getBigDecimal(bd1.doubleValue());
		return getBigDecimal(bd1.add(bd2).doubleValue());
		
	}
	public static BigDecimal multiplyBigDecimal(BigDecimal bd1, BigDecimal bd2){
		if(bd1==null || bd2==null)
			return getBigDecimal(new Double(0.0));
		return getBigDecimal(bd1.multiply(bd2).doubleValue());
		
	}
	
	

	public static Double multiplyDouble(Double bd1, Double bd2, MathContext mathContext){
		if(bd1==null || bd2==null)
			return new Double(0.0);
		return getBigDecimal(bd1).multiply(getBigDecimal(bd2),mathContext).doubleValue();
	}
	
	public static Double multiplyDouble(Double bd1, Double bd2){
		if(bd1==null || bd2==null)
			return new Double(0.0);
		return getBigDecimal(bd1).multiply(getBigDecimal(bd2)).doubleValue();
	}
	
	public static Double divideDouble(Double bd1, Double bd2){
		if(bd1==null || bd2==null)
			return new Double(0.0);
		return getBigDecimal(bd1).divide(getBigDecimal(bd2),MathContext.DECIMAL64).doubleValue();
		
	}
	
	public static BigDecimal divideBigDecimal(BigDecimal bd1, BigDecimal bd2){
		if(bd1==null || bd2==null)
			return getBigDecimal(new Double(0.0));
		return getBigDecimal(bd1.divide(bd2,MathContext.DECIMAL64).doubleValue());
		
	}
	
	public static String numberToMonedaString(Double numero){
        DecimalFormatSymbols  symbols =new  DecimalFormatSymbols();
        symbols.setDecimalSeparator(',');
        symbols.setGroupingSeparator('.');
        String pattern="#,###,###,###,##0.00;#,-###,###,###,##0.00";
        DecimalFormat weirdFormatter =  new DecimalFormat(pattern,symbols);
        String numeroString="";
        if(numero!=null)                
        	numeroString ="$ "+weirdFormatter.format(new Double(numero));	
        return numeroString;
	}
	
	public static String numberToString(Double numero){
        DecimalFormatSymbols  symbols =new  DecimalFormatSymbols();
        symbols.setDecimalSeparator(',');
        symbols.setGroupingSeparator('.');
        String pattern="#,###,###,###,##0.00;#,-###,###,###,##0.00";
        DecimalFormat weirdFormatter =  new DecimalFormat(pattern,symbols);
        String numeroString="";
        if(numero!=null)                
        	numeroString =weirdFormatter.format(new Double(numero));	
        return numeroString;
	}
	
	public static String numberToStringSinFormato(Double numero){
        DecimalFormatSymbols  symbols =new  DecimalFormatSymbols();
        symbols.setDecimalSeparator(',');
        symbols.setGroupingSeparator('.');
        String pattern="#0;#,-#0";
        DecimalFormat weirdFormatter =  new DecimalFormat(pattern,symbols);
        String numeroString="";
        if(numero!=null)                
        	numeroString =weirdFormatter.format(new Double(numero));	
        return numeroString;
	}
	
	public static String formatDate(Date fecha){
		return DateUtil.formatDate(fecha, DateUtil.ddSMMSYYYY_MASK);
	}
	
	public static BufferedImage resize(BufferedImage image, int width, int height) {
		int type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType();
		BufferedImage resizedImage = new BufferedImage(width, height, type);
		Graphics2D g = resizedImage.createGraphics();
		g.setComposite(AlphaComposite.Src);

		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
		RenderingHints.VALUE_INTERPOLATION_BILINEAR);

		g.setRenderingHint(RenderingHints.KEY_RENDERING,
		RenderingHints.VALUE_RENDER_QUALITY);

		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
		RenderingHints.VALUE_ANTIALIAS_ON);

		g.drawImage(image, 0, 0, width, height, null);
		g.dispose();
		return resizedImage;
	}
	
public static BufferedImage getImage(byte[] data) throws Exception
	{
		BufferedImage bi = null;
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
		bi = ImageIO.read(bais);	    
		return bi;
	}
	

	public static BufferedImage acomodarFoto(byte[] bs, Integer anchoFinal,Integer altoFinal) throws Exception {

		BufferedImage imagenOriginal = getImage(bs);
		Integer anchoFinalAux=anchoFinal;
		Integer altoFinalAxu=altoFinal;
		Integer anchoTemporal;
		Integer altoTemporal;
		Double ratio = Integer.valueOf(imagenOriginal.getHeight()).doubleValue()/ Integer.valueOf(imagenOriginal.getWidth()).doubleValue();

		if ((altoFinalAxu == 0) && (anchoFinalAux != 0)) {
			altoFinal = anchoFinal * imagenOriginal.getHeight()/ imagenOriginal.getWidth();
		}
		if ((altoFinalAxu != 0) && (anchoFinalAux == 0)) {
			anchoFinal = altoFinal * imagenOriginal.getWidth()/ imagenOriginal.getHeight();
		}
		if (((altoFinalAxu == 0) && (anchoFinalAux == 0)) || altoFinalAxu < 0 || anchoFinalAux < 0) {
			return imagenOriginal;
		}

		BufferedImage img=null;
		
		if (imagenOriginal.getWidth()<imagenOriginal.getHeight()) {
            anchoTemporal=anchoFinal;
            altoTemporal=Double.valueOf(anchoFinal.doubleValue()*ratio).intValue();
            BufferedImage imagenResized = resize(imagenOriginal, anchoTemporal, altoTemporal);
            img = new BufferedImage(anchoFinal, altoFinal, imagenOriginal.getType());
            Graphics2D g = img.createGraphics();
            Integer yFinal=(imagenResized.getHeight()-altoFinal)/2;
            g.drawImage(imagenResized, 0, 0, anchoFinal, altoFinal, 0, yFinal, anchoFinal,altoFinal, null);
            g.dispose();
        }else{
            altoTemporal=altoFinal;
            anchoTemporal=Double.valueOf(altoFinal.doubleValue()/ratio).intValue();
            BufferedImage imagenResized = resize(imagenOriginal, anchoTemporal, altoTemporal);
            img = new BufferedImage(anchoFinal, altoFinal, imagenOriginal.getType());
            Graphics2D g = img.createGraphics();
            Integer xFinal=(imagenResized.getWidth()-anchoFinal)/2;
            g.drawImage(imagenResized, 0, 0, anchoFinal, altoFinal, xFinal, 0, anchoFinal,altoFinal, null);
            g.dispose();
        }
		return img;
	}
	
	
	public static Integer[] validarPeriodo(String periodo){
		Integer mes = null;
		Integer anio = null;
		BusinessException be = new BusinessException();
		if(periodo != null && periodo.trim().length() > 0){
			periodo = periodo.trim();
			int len = periodo.length();
			if(len >= 6 && len <= 7){
				String[] periodoSplit = periodo.split("/");
				if(periodoSplit.length == 2){
					periodoSplit[0] = periodoSplit[0].trim();
					periodoSplit[1] = periodoSplit[1].trim();
					if(periodoSplit[0].length() == 1 || periodoSplit[0].length() == 2){
						if(periodoSplit[1].length() == 4){
							String strMes = periodoSplit[0];
							String strAnio = periodoSplit[1];
							try{
								mes = Integer.parseInt(strMes);
								anio = Integer.parseInt(strAnio);
								Integer[] periodoArray = {mes, anio};
								return periodoArray;
							}catch(NumberFormatException ne){
								be.addExceptionKey("Periodo.periodoIncorrecto.error");
								//return null;
							}
						} else {
							be.addExceptionKey("Perido.periodoIncorrecto.error");
						}
					} else {
						be.addExceptionKey("Periodo.periodoIncorrecto.error");
					}
				}else{
					be.addExceptionKey("Periodo.periodoIncorrecto.error");
				}
			} else {
				be.addExceptionKey("Periodo.periodoIncorrecto.error");
			}
		}
		CommonGeneralUtil.throwBussinesException(be);
		return null;
	}

	
	public  static Boolean validateEmail(String email){
		return EMAIL_PATTERN.matcher(email).find();
	}
	
	 public static String getMesEnLetra(int mes){
	    	try {			    	    		    	
			    if (mes==1){
			    	return "Enero";
			    } else if (mes==2){
			    	return "Febrero";
			    } else if (mes==3){
			    	return "Marzo";
			    }else if (mes==4){
			    	return "Abril";
			    }else if (mes==5){
			    	return "Mayo";
			    }else if (mes==6){
			    	return "Junio";
			    }else if (mes==7){
			    	return "Julio";
			    }else if (mes==8){
			    	return "Agosto";
			    }else if (mes==9){
			    	return "Septiembre";
			    }else if (mes==10){
			    	return "Octubre";
			    }else if (mes==11){
			    	return "Noviembre";
			    }else if (mes==12){
			    	return "Diciembre";
			    }
			    
	    	} catch (Exception e){    		
	    	}
	    	return "";
	    }
	 
	 
	 public static Object clone(Object o)
	 {
	   Object clone = null;
	  
	   try
	   {
	      clone = o.getClass().newInstance();
	   }
	   catch (InstantiationException e)
	   {
	      e.printStackTrace();
	   }
	   catch (IllegalAccessException e)
	   {
	      e.printStackTrace();
	   }
	  
	   // Walk up the superclass hierarchy
	   for (Class obj = o.getClass();
	     !obj.equals(Object.class);
	     obj = obj.getSuperclass())
	   {
	     Field[] fields = obj.getDeclaredFields();
	     for (int i = 0; i < fields.length; i++)
	     {
	       fields[i].setAccessible(true);
	       try
	       {
	         // for each class/suerclass, copy all fields
	         // from this object to the clone
	         fields[i].set(clone, fields[i].get(o));
	       }
	       catch (IllegalArgumentException e){}
	       catch (IllegalAccessException e){}
	     }
	   }
	   return clone;
	 }
	 
		
		public static String primeraLetraMinuscula(String texto){
			try{
			 return texto.substring(0, 1).toLowerCase() +  texto.substring(1);
			}catch (Exception e) {
				return texto;
			}
		}

		public static String primeraLetraMayuscula(String texto){
			try{
			 return texto.substring(0, 1).toUpperCase() +  texto.substring(1);
			}catch (Exception e) {
				return texto;
			}
		}
		
		
		public static Object  getObjectByIdString(String nombreClaseCompleto, String id){
			try{
				Class clase = Class.forName(nombreClaseCompleto);
				Object service =getService(CommonGeneralUtil.primeraLetraMinuscula(clase.getSimpleName())+"Service");
				Method metodoFindById = service.getClass().getMethod("findById", Long.class);
				Object objectSelect= metodoFindById.invoke(service, Long.valueOf(id));
				return objectSelect;
			}
			catch (Exception e) {				
				return null;
			}
			
		}

		public static Object getService(String bean) {
			return Application.getBean(bean);
		}
		
		public static String getDescripcionObjectPersistent(Class claseReferente, String id, String nombreAtributoDescripcion){
		try{
			Class clase= claseReferente;
			Object service =CommonGeneralUtil.getService(CommonGeneralUtil.primeraLetraMinuscula(clase.getSimpleName())+"Service");
			Method metodoFindById = service.getClass().getMethod("findById", Long.class);
			Object objectSelect= metodoFindById.invoke(service, Long.valueOf(id));
			Method metodoPropertie = objectSelect.getClass().getMethod("get" + CommonGeneralUtil.primeraLetraMayuscula(nombreAtributoDescripcion));
			Object descripcionObject = metodoPropertie.invoke(objectSelect);
			if(descripcionObject!=null){
				if(descripcionObject instanceof String){
					return descripcionObject.toString();
				}
				else if(descripcionObject instanceof Long || descripcionObject instanceof Integer){
					return descripcionObject.toString();
				}
				else if(descripcionObject instanceof Double){
					return CommonGeneralUtil.numberToString((Double)descripcionObject);
				}
				else if(descripcionObject instanceof Date){
					return CommonGeneralUtil.formatDate((Date)descripcionObject);
				}
				else{
					return descripcionObject.toString();
				}
			}
			else{
				return "";
			}
		}catch (Exception e) {
			return "";
		}
		}
}